﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SetListManager.Models;
using SetListManager.Helpers;

namespace SetListManager.Controllers
{
    [BandAuthorize]
    public class SongsController : Controller
    {
        //
        // Our Database context object
        // 
        private SLMDbContext db = new SLMDbContext();

        //
        // GET: /Song/

        public ActionResult Index()
        {
            var query = from s in db.Songs.Include("Band")
                        .Include("Usage")
                        where s.BandID == CurrentBand.ID
                        orderby s.Title descending
                        select s;

            return View(query);
        }

        public ActionResult Index2()
        {
            return View();
        }

        /// GET: /Song/RetrieveSongList
        /// <summary>
        /// Retrieves song list asynchronously via AJAX, formatted in JSON for the DataTable jQuery code.
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public JsonResult RetrieveSongList(jQueryDataTableParamModel param)
        {
            var result = from s in db.Songs
                         where s.BandID == CurrentBand.ID
                         orderby s.Title descending
                         // Flatten out data a bit, only selecting necessary data so we send less down the wire
                         select new { s.SongID, s.Title, s.Soloist, s.Key, UsageText = s.Usage.UsageText, s.Tempo, s.Genre, s.Duration };

            return Json(new
            {
                sEcho = param.sEcho,
                iTotalRecords = result.Count(),
                iTotalDisplayRecords = result.Count(),
                aaData = result
            },
            JsonRequestBehavior.AllowGet);
        }

        /// GET: /Song/RetrieveSong/5
        /// <summary>
        /// Retrieves song asynchronously via AJAX, formatted in JSON.
        /// </summary>
        /// <param name="p_songID">The unique id of the song to retrieve</param>
        /// <returns></returns>
        public JsonResult RetrieveSong(int id)
        {
            var result = from s in db.Songs
                         where s.SongID == id
                         
                         // Flatten out data a bit, only selecting necessary data so we send less down the wire
                         select new { s.SongID, s.Title, s.Soloist, s.Key, UsageText = s.Usage.UsageText, s.Tempo, s.Genre, s.Duration };

            return Json(result, JsonRequestBehavior.AllowGet);
        }
        private struct jsonSong
        {
            public int ID;
            public string Title;
            public string Key;
            public string Soloist;
        };
        /// POST: /Song/EditSong/5
        /// <summary>
        /// Edits a song by passing in the field data to be applied. The song data is passed as json.
        /// First this method parses the json to a song object, and then uses the ID to retrieve the song, and then update it.
        ///
        /// </summary>
        /// <param name="jsonData">Contains a Song object in JSON string form.</param>
        /// <returns>true if edit succeeds; otherwise, false.</returns>
        ///
        [HttpPost]
        public bool EditSong(string jsonData)
        {
            
            System.Web.Script.Serialization.JavaScriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer();
            jsonSong sEditData = jss.Deserialize<jsonSong>(jsonData);
            var sCurrent = db.Songs.Where(m => m.SongID == sEditData.ID).First();
            //Apply changes to each field
            sCurrent.Key = sEditData.Key;
            sCurrent.Title = sEditData.Title;
            sCurrent.Soloist = sEditData.Soloist;
            db.SaveChanges();

            return true ;
        }

        //
        // GET: /Song/Details/5

        public ActionResult Details(int id)
        {
            // Explicitly load our navigation property "Band"
            Song song = (from s in db.Songs.Include("Band")
                        .Include("Usage")
                         where s.SongID == id
                         select s).First();

            // NOTE: We needed .First() above so it only returns one song.  Otherwise it would
            // be a collection of songs (perhaps with only one in it).
            return View(song);
        }

        //
        // GET: /Song/Create


        public ActionResult Create()
        {
            SongAndUsageTypes sut = new SongAndUsageTypes();
            sut.song = new Song();

            // Fill in usagetypes

            //Create a select list of the usages for the view to retrieve.
            sut.usagetypes = from u in db.UsageTypes
                            select u;

            // Retrieve band data.  We need this to populate some fields in our view.
            sut.song.Band = (from b in db.Bands
                      where b.BandID == CurrentBand.ID
                      select b).First();

            return View(sut);
        } 

        //
        // POST: /Song/Create

        [HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            if (ModelState.IsValid)
            {
                // Copy over values
                Song song = new Song();
                bool bReturn = TryUpdateModel(song, "song",
                    new string[] { "Title", "Key", "Soloist", "Tempo", "Genre", "Note1", "Note2", "Note3", "Note4", "Note5" },
                    collection.ToValueProvider());

                // Parse duration
                song.Duration = new MinSec(0,0);
                string duration = collection["duration_str"];
                if (duration != null && duration.Length > 0)
                {
                    // HACK: Manually update duration, since it's coming in as a string and we need to parse into a complex type
                    // We already have a good TimeSpan parser in our custom ModelBinder, so use that one
                    TimeSpan ts;
                    MyTimeSpanModelBinder.TryParseTime(duration, out ts);
                    song.Duration.Minutes = ts.Hours; // TryParseTime gives us hours:minutes, so we need to fudge here
                    song.Duration.Seconds = ts.Minutes;
                }

                // Fill in missing details so we can insert into DB
                song.BandID = CurrentBand.ID;
                Int32 usage = Int32.Parse(collection["usagetypes"]);
                song.Usage = (from u in db.UsageTypes
                          where u.UsageTypeID == usage
                          select u).First();

                // Add to DB
                db.Songs.Add(song);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            // Error -- redirect.  First fill in values for form
            SongAndUsageTypes sut = new SongAndUsageTypes();
            sut.duration_str = collection["duration_str"];
            bool ret = TryUpdateModel(sut.song,
                    new string[] { "Title", "Key", "Soloist", "Tempo", "Genre", "Note1", "Note2", "Note3", "Note4", "Note5" },
                    collection.ToValueProvider());
            sut.usagetypes = from u in db.UsageTypes
                                              select u;
            return View(sut); // Kick them back to fix their errors
        }        
        //
        // GET: /Song/Edit/5
        // NOTE: This is a temporary implementation of song editing using the Song/Edit.aspx view
        // The real song editing will use Ajax and JSON with a pop-up window directly from Song/Index.aspx view
 
        public ActionResult Edit(int id)
        {
            SongAndUsageTypes sl = new SongAndUsageTypes();

            // Be sure to .include band so we pull in the "Note" field descriptors
            sl.song = (from s in db.Songs
                       .Include("Band")
                       .Include("Usage")
                       where s.SongID == id
                       select s).First();

            //Create a select list of the usages for the view to retrieve.
            sl.usagetypes = from u in db.UsageTypes
                         select u;

            // Hack: pass duration as string, so our view can display it easier, and we can parse it here easier
            sl.duration_str = sl.song.Duration.FormatMinSec;
          
            return View(sl);
        }

        //
        // POST: /Song/Edit/5
        // NOTE: This is a temporary implementation of song editing using the Song/Edit.aspx view

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(int id, FormCollection collection)
        {
            //bugbug we assume the id was submitted by the owner, but really we should double check that the owner has permissions.
            //especially as someone could have intercepted and changed the id to a song that they don't own over the wire to be malicious.
            //problem that should be part of the model as part of the TryUpdateModel call below.
            Song song = (from s in db.Songs
                         where s.SongID == id
                         select s).First();

            bool bReturn = TryUpdateModel(song,
                new string[] { "Title", "Key", "Soloist", "Tempo", "Genre", "Note1", "Note2", "Note3", "Note4", "Note5" },
                collection.ToValueProvider());

            // HACK: manually update usage type.
            Int32 usage = Int32.Parse(collection["Usage"]);
            song.Usage = (from u in db.UsageTypes
                          where u.UsageTypeID == usage
                          select u).First();

            // HACK: Manually update duration, since it's coming in as a string and we need to parse into a complex type
            // We already have a good TimeSpan parser in our custom ModelBinder, so use that one
            TimeSpan ts;
            string dur = collection["Duration"];
            MyTimeSpanModelBinder.TryParseTime(dur, out ts);
            song.Duration.Minutes = ts.Hours; // TryParseTime gives us hours:minutes, so we need to fudge here
            song.Duration.Seconds = ts.Minutes;

            int numRecordsChanged = db.SaveChanges();
            return RedirectToAction("Index");
        }

        //
        // GET: /Song/Delete/5
 
        public ActionResult Delete(int id)
        {
            // Query for the specified song
            var result = (from s in db.Songs
                               where s.SongID == id
                               select s).First();
            return View(result);
        }

        //
        // POST: /Song/Delete/5

        [HttpPost]
        public ActionResult Delete(int id, FormCollection collection)
        {
            // Remove song record from table
            try
            {
                var song = db.Songs.Find(id);
                db.Songs.Remove(song);
                int i = db.SaveChanges();
                return RedirectToAction("Index");
            }
            catch
            {
                // TODO: handle error gracefully
                return RedirectToAction("Index");
            }
        }

        //
        // GET: /Song/Groups/

        public ActionResult Groups()
        {
            // Grab only songs in groups
            var query = from s in db.Songs.Include("Band")
                        .Include("Usage")
                        .Include("Group")
                        where s.Group != null
                        orderby s.Group.GroupID, s.GroupOrder ascending
                        select s;

            return View(query);
        }


    }
}
